import _ from 'lodash';

import { QueuingState } from './queuingState';
import { RunningState } from './runningState';
import { JsonFileUtility } from '$common';
import { BaseSimulationJobState } from './baseJobState';
import { SimulationModelService } from '../service/modelService';
import { DriverService, DriverLaunchParameter } from '$domain/model/driver';
import { JobStatus, ParameterType, SimulationParameter, SimulationSpec } from '../type';
export class PreparingState extends BaseSimulationJobState {

  constructor() { super('PrearingState'); }

  private async buildDriverSetupConfig(definition: SimulationSpec) {

    const simulationSpec = _.cloneDeep(definition);

    simulationSpec.model.file = await new SimulationModelService(simulationSpec.model)
      .unzipModelIfNecessary(this.simulationPath.getSimModelDir());

    const fileParameters = simulationSpec.parameters.filter(f => f.type === ParameterType.FILE);
    const downloadedFileParameters = await this.simParameterService.downloadFileParameters(fileParameters);

    simulationSpec.parameters = this.simParameterService.leftMergeParameters<SimulationParameter>(simulationSpec.parameters, downloadedFileParameters);

    return Object.assign({ driverName: this.product }, simulationSpec) as DriverLaunchParameter;
  }

  async doRun() {
    this.simJobRepo.updateJobStatus(this.jobId, JobStatus.Prepareing);

    const simDefinition = this.simJobRepo.getById(this.jobId)?.definition;
    const simDriverParameters: DriverLaunchParameter = await this.buildDriverSetupConfig(simDefinition);
    this.logger.debug(`✅ (DriverConverter) ==> (jobId=${this.jobId})convert SimulationRequest to DriverLaunchParamter: ${JSON.stringify(simDriverParameters, null, 2)}`);

    const simConfigFile = this.simulationPath.getSimInputFile();
  
    JsonFileUtility.serializeToFile(simConfigFile, simDriverParameters);
    this.logger.debug(`🎉 (jobId=${this.jobId})write DriverLaunchParamter to ${simConfigFile} successfully.`);

    const canConcurrent = DriverService.canConcurrent(this.product);
    const maxConcurrent = DriverService.getMaxConcurrent(this.product);
    const runningJobs = this.simJobRepo.findRunningJobsBy(this.product);

    if (_.isEmpty(runningJobs)) {
      await this.doNextStateOperation(new RunningState());
      return;
    }
    if (canConcurrent && runningJobs.length < maxConcurrent) {
      await this.doNextStateOperation(new RunningState());
    } else {
      this.logger.warn(`❗️ Job exception: the number of running jobs is ${runningJobs.length}, expected to be ${maxConcurrent}`);
      this.logger.debug(`🔸 The driver(${this.product}) is being referenced by jobId(${runningJobs[0].id}).`);
      this.logger.debug(`🔸 Current job(${this.jobId}) immediately enters the queue and waits.`);
      await this.doNextStateOperation(new QueuingState());
    }
  }
}